﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using SharpDX;
using SharpDX.D3DCompiler;
using SharpDX.Direct3D11;
using SharpDX.DXGI;

namespace SharpDX
{
    public class CD3D
    {
        public SwapChain m_swapchain;
        public SharpDX.Direct3D11.Device m_device;
        public DeviceContext m_devicecontext;
        public RenderTargetView m_renderTargetView;
        public Texture2D m_depthStencilBuffer;
        public DepthStencilState m_depthStencilState;
        public DepthStencilView m_depthStencilView;
        public RasterizerState m_rasterState;
        public Matrix m_projectionMat;
        public Matrix m_worldMat;
        public Matrix m_orthoMat;

        public CD3D()
        {

        }
        public bool Initialize(IntPtr hwnd,int screenWidth,int screenHeight)
        {
            var swapChainDesc = new SwapChainDescription
            {
                BufferCount=1,
                Flags= SwapChainFlags.None,
                IsWindowed=true,
                OutputHandle=hwnd,
                ModeDescription=new ModeDescription(screenWidth,screenHeight,new Rational(60,1),Format.R8G8B8A8_UNorm),
                SampleDescription=new SampleDescription(1,0),
                SwapEffect= SwapEffect.Discard,
                Usage= Usage.RenderTargetOutput
            };

            SharpDX.Direct3D11.Device.CreateWithSwapChain(Direct3D.DriverType.Hardware, DeviceCreationFlags.BgraSupport, swapChainDesc, out m_device, out m_swapchain);
            m_devicecontext = m_device.ImmediateContext;

            var backBuffer = Texture2D.FromSwapChain<Texture2D>(m_swapchain, 0);

            m_renderTargetView = new RenderTargetView(m_device, backBuffer);

            backBuffer.Dispose();

            var depthBufferDesc = new Texture2DDescription()
            {
                ArraySize=1,
                BindFlags= BindFlags.DepthStencil,
                CpuAccessFlags= CpuAccessFlags.None,
                Format= Format.D24_UNorm_S8_UInt,
                Height=screenHeight,
                MipLevels=1,
                OptionFlags= ResourceOptionFlags.None,
                SampleDescription=new SampleDescription(1,0),
                Usage= ResourceUsage.Default,
                Width=screenWidth
            };

            m_depthStencilBuffer = new Texture2D(m_device, depthBufferDesc);

            var depthStencilDesc = new DepthStencilStateDescription()
            {
                BackFace = new DepthStencilOperationDescription()
                {
                    Comparison = Comparison.Always,
                    DepthFailOperation = StencilOperation.Decrement,
                    FailOperation = StencilOperation.Keep,
                    PassOperation = StencilOperation.Keep
                },
                DepthComparison = Comparison.Less,
                DepthWriteMask= DepthWriteMask.All,
                FrontFace = new DepthStencilOperationDescription()
                {
                    Comparison= Comparison.Always,
                    DepthFailOperation= StencilOperation.Increment,
                    FailOperation= StencilOperation.Keep,
                    PassOperation= StencilOperation.Keep,
                },
                 IsDepthEnabled=true,
                 IsStencilEnabled=true,
                 StencilReadMask=0xFF,
                 StencilWriteMask=0xFF
            };
            m_depthStencilState = new DepthStencilState(m_device, depthStencilDesc);

            m_devicecontext.OutputMerger.SetDepthStencilState(m_depthStencilState, 1);

            var depthStencilViewDesc = new DepthStencilViewDescription()
            {
                 Dimension= DepthStencilViewDimension.Texture2D,
                  Format= Format.D24_UNorm_S8_UInt,
                 Texture2D = new DepthStencilViewDescription.Texture2DResource()
                 {
                    MipSlice=0
                 }
            };
            m_depthStencilView = new DepthStencilView(m_device, m_depthStencilBuffer, depthStencilViewDesc);

            m_devicecontext.OutputMerger.SetTargets(m_depthStencilView, m_renderTargetView);

            var rasterDesc = new RasterizerStateDescription()
            {
                IsAntialiasedLineEnabled = false,
                CullMode = CullMode.Back,
                DepthBias = 0,
                DepthBiasClamp = .0f,
                IsDepthClipEnabled = true,
                FillMode = FillMode.Solid,
                IsFrontCounterClockwise = false,
                IsMultisampleEnabled = false,
                IsScissorEnabled = false,
                SlopeScaledDepthBias = .0f
            };

            m_rasterState = new RasterizerState(m_device, rasterDesc);
            m_devicecontext.Rasterizer.State = m_rasterState;
            m_devicecontext.Rasterizer.SetViewport(0, 0, screenWidth, screenHeight, 0, 1);

            m_projectionMat = Matrix.PerspectiveFovLH((float)(Math.PI / 4), (float)(screenWidth) / screenHeight, 0.1f, 1000);
            m_worldMat = Matrix.Identity;
            m_orthoMat = Matrix.OrthoLH(screenWidth, screenHeight, 0.1f, 1000);
            return true;
        }
        public void ShutDown()
        {
            
            if(m_rasterState!=null)
            {
                m_rasterState.Dispose();
                m_rasterState = null;
            }
            if (m_depthStencilView != null)
            {
                m_depthStencilView.Dispose();
                m_depthStencilView = null;
            }

            if (m_depthStencilState != null)
            {
                m_depthStencilState.Dispose();
                m_depthStencilState = null;
            }

            if (m_depthStencilBuffer != null)
            {
                m_depthStencilBuffer.Dispose();
                m_depthStencilBuffer = null;
            }

            if (m_renderTargetView != null)
            {
                m_renderTargetView.Dispose();
                m_renderTargetView = null;
            }

            if (m_device != null)
            {
                m_device.Dispose();
                m_device = null;
            }
            if (m_swapchain != null)
            {
                m_swapchain.Dispose();
                m_swapchain = null;
            }

        }
        public void BeginScene(Color4 color)
        {
            m_devicecontext.ClearDepthStencilView(m_depthStencilView, DepthStencilClearFlags.Depth, 1, 0);
            m_devicecontext.ClearRenderTargetView(m_renderTargetView, color);
        }

        public void EndScene()
        {
            m_swapchain.Present(0, PresentFlags.None);
        }

    }
}
